package cn.yx.common.core.util.http.httpclient;

import cn.hutool.core.collection.CollectionUtil;
import cn.yx.common.core.enums.CharsetEnum;
import cn.yx.common.core.enums.MimeTypeEnum;
import cn.yx.common.core.enums.http.HttpHeaderEnum;
import cn.yx.common.core.exception.CommonException;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.List;
import java.util.Map;

/**
 * <p>HttpBasic，为HttpGetUtil，HttpPostUtil等类的父类</p>
 *
 * @author Wgssmart
 */
@Slf4j
public class HttpBasic {

    /**
     * 用于接收服务端返回的cookies数据
     */
    private static List<Cookie> cookies;

    /**
     * 获取服务端返回的cookie数据
     *
     * @return 服务端返回的cookie数据
     */
    public static List<Cookie> getCookies() {
        return cookies;
    }

    public static void setCookies(List<Cookie> cookies) {
        HttpBasic.cookies = cookies;
    }

    /**
     * 默认的RequestConfig配置
     */
    public static RequestConfig requestConfig = RequestConfig.custom()
            .setSocketTimeout(15000)
            .setConnectTimeout(15000)
            .setConnectionRequestTimeout(15000)
            .build();

    /**
     * 重设RequestConfig的超时时间
     *
     * @param millis 毫秒数
     */
    public static void timeout(int millis) {
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(millis)
                .setConnectTimeout(millis)
                .setConnectionRequestTimeout(millis)
                .build();
    }

    /**
     * 刷新headers，可能需要填充Content-Type和Accept
     *
     * @param headers
     * @param dataTypeEnum
     */
    public static void refreshHeaders(Map<String, String> headers, MimeTypeEnum dataTypeEnum) {
        if (CollectionUtil.isNotEmpty(headers)) {
            if (!headers.containsKey(HttpHeaderEnum.CONTENT_TYPE.getName())) {
                headers.put(HttpHeaderEnum.CONTENT_TYPE.getName(), dataTypeEnum.getMime() + "; charset=" + CharsetEnum.UTF8.getValue());
            }
            if (!headers.containsKey(HttpHeaderEnum.ACCEPT.getName())) {
                headers.put(HttpHeaderEnum.ACCEPT.getName(), dataTypeEnum.getMime());
            }
        }
    }

    /**
     * 设置请求头
     *
     * @param httpGet
     * @param headers
     */
    public static void setHeaders(HttpGet httpGet, Map<String, String> headers) {
        if (CollectionUtil.isNotEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpGet.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 设置请求头
     *
     * @param httpPost
     * @param headers
     */
    public static void setHeaders(HttpPost httpPost, Map<String, String> headers) {
        if (CollectionUtil.isNotEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpPost.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 设置请求头
     *
     * @param httpPut
     * @param headers
     */
    public static void setHeaders(HttpPut httpPut, Map<String, String> headers) {
        if (CollectionUtil.isNotEmpty(headers)) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpPut.addHeader(entry.getKey(), entry.getValue());
            }
        }
    }

    /**
     * 获取CookieStore
     *
     * @param cookieList
     * @return
     */
    public static CookieStore getCookieStore(List<Cookie> cookieList) {
        CookieStore cookieStore = new BasicCookieStore();
        if (CollectionUtil.isNotEmpty(cookieList)) {
            for (Cookie cookie : cookieList) {
                cookieStore.addCookie(cookie);
            }
        }
        return cookieStore;
    }

    /**
     * 获取SSL连接
     *
     * @param certPath     证书路径
     * @param certPassword 证书密钥
     * @param certType     证书类型，可参考{@link cn.yx.common.core.enums.security.CertTypeEnum}
     * @param sslProtocols SSL协议，可参考{@link cn.yx.common.core.enums.security.SslProtocolEnum}
     * @return
     */
    public static SSLConnectionSocketFactory getSslConnectionSocketFactory(String certPath, String certPassword,
                                                                           String certType, String[] sslProtocols) {
        try {
            KeyStore keyStore = KeyStore.getInstance(certType);
            try (InputStream inputStream = new FileInputStream(certPath)) {
                keyStore.load(inputStream, certPassword.toCharArray());
            }
            // Trust own CA and all self-signed certs
            SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, certPassword.toCharArray()).build();
            // Allow TLSv1 protocol only
            return new SSLConnectionSocketFactory(sslContext, sslProtocols,
                    null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException |
                 UnrecoverableKeyException | KeyManagementException e) {
            log.error("read certification or create ssl connection socket factory error: {}", e.getMessage());
            throw new CommonException("", e);
        }
    }

    /**
     * 获取String字符串响应的ResponseHandler<br/>
     * 如果状态为200，则直接获取response结果，如果状态为302，则获取重定向的url，其他状态暂时返回非正常响应状态
     */
    public static final ResponseHandler<String> STRING_RESPONSE_HANDLER = response -> {
        int status = response.getStatusLine().getStatusCode();
        if (status == HttpStatus.SC_OK) {
            HttpEntity entity = response.getEntity();
            return entity != null ? EntityUtils.toString(entity, CharsetEnum.UTF8.getValue()) : null;
        } else if (status == HttpStatus.SC_MOVED_TEMPORARILY) {
            return response.getFirstHeader("Location").getValue();
        } else {
            throw new ClientProtocolException("no normal status: " + status);
        }
    };

}
